package com.chen.common.feign.config;

import cn.hutool.core.util.ArrayUtil;
import com.chen.common.feign.annotation.FeignOption;
import feign.Request;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author banmoon
 * @date 2024/04/10 18:24:44
 */
@Slf4j
@Aspect
@Component
public class FeignOptionAop {

    @Around("@annotation(com.chen.common.feign.annotation.FeignOption)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        Class<?>[] types = method.getParameterTypes();
        Object[] args = joinPoint.getArgs();

        // 判断方法的所有入参中是否存在Options，并且实际传参是不是null
        if (ArrayUtil.isNotEmpty(types)) {
            int index = -1;
            Class<?> type = null;

            // 找出哪个参数是Options及索引位
            for (int i = 0; i < types.length; i++) {
                Class<?> paramType = types[i];
                if (Objects.equals(paramType, Request.Options.class)){
                    type = paramType;
                    index = i;
                    break;
                }
            }

            // 如果存在Options参数并且实际传参为null，则读取注解中的信息并设置进去
            if (type != null && Objects.isNull(args[index])) {
                FeignOption option = method.getAnnotation(FeignOption.class);

                // 构建超时对象
                long connectTimeout = option.connectTimeout();
                TimeUnit connectTimeoutUnit = option.connectTimeoutUnit();
                long readTimeout = option.readTimeout();
                TimeUnit readTimeoutUnit = option.readTimeoutUnit();
                args[index] = new Request.Options(connectTimeout, connectTimeoutUnit, readTimeout, readTimeoutUnit, true);
            }
        }
        return joinPoint.proceed(args);
    }

}